home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-06-24 | 34.4 KB | 728 lines | [TEXT/CCL ] |
- ; (c) Copyright 1990 by University of Massachusetts. All rights reserved.
- ; This software was conceived, designed, and written by Dan Suthers
- ; while supported by the National Science Foundation under grant number
- ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
- ; CA. Partial support was also received from the Office of Naval Research
- ; under a University Research Initiative Grant, contract N00014-86-K-0764.
- ; Mr. Suthers created this software under his own initiative while in an
- ; academic relationship with the University of Massachusetts. The above
- ; copyright notice was a condition placed by University lawyers on approval
- ; of distribution of this software by Apple Computer, and is not meant to
- ; imply that this software was created in an employment or "work for hire"
- ; relationship between the University and Mr. Suthers.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; File: Hyper-Display.lisp
- ; Author: Dan Suthers
- ; Created: 15-Apr-89 14:07:15
- ; Modified: 24-Jun-90 02:11:26 (Dan Suthers)
- ; Language: Common Lisp
- ; Package: USER
- ;
- ; Description: A Hyper-text like display and browsing interface. The
- ; application provides a hierarchically structured representation of
- ; some text, and command functions. Regions of text (possibly nested)
- ; have structures with pointers to application data structures associated
- ; with them. Hyper-Display displays the text in a specialized Fred window.
- ; When the user selects a range of the text with a mouse, Hyper-Display is
- ; able to determine the smallest structure in the hierarchy which encloses
- ; that region. When the user initiates a query on this selected region of
- ; text, the command function is called on the structure representing the
- ; selected region. For example, a command could be a request to define a
- ; term, and the function could replace the text in the window with a
- ; definition, preserving a pointer to the previous hyper-structure, so
- ; that another command can return to the original text if desired.
- ;
- ; (c) Copyright 1989, by Daniel D. Suthers
- ; Department of Computer and Information Science
- ; University of Massachusetts
- ; Amherst, Massachusetts 01003
- ; All rights reserved.
- ;
- ; This software was conceived, designed, and written by Dan Suthers
- ; while supported by the National Science Foundation under grant number
- ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
- ; CA. Partial support was also received from the Office of Naval Research
- ; under a University Research Initiative Grant, contract N00014-86-K-0764.
- ; I wish to acknowledge the generous support of Beverly Woolf, who obtained
- ; the above grants and encouraged me to pursue my own research interests in
- ; her lab. This work would not have been possible without the resources and
- ; stimulating environment of the Computer and Information Science department.
- ;
- ; Permission to use, modify, and distribute this software is granted subject
- ; to the following restrictions and understandings:
- ; 1. The file header, including this notice, shall be retained, and may be
- ; extended to include documentation of modifications to the software.
- ; 2. This material is for nonprofit educational and research purposes only.
- ; Users are requested, but not required, to inform Mr. Suthers of any
- ; noteworthy uses of this software.
- ; 3. Mr. Suthers and the University of Massachusetts make no warantee or
- ; representation that the operation of this software will be error free,
- ; and are under no obligation to provide any services.
- ; 4. Any user of such software agrees to indemnify and hold harmless Mr.
- ; Suthers and the University of Massachusetts from all claims arising
- ; out of the use or misuse of this software, or arising out of any
- ; accident, injury, or damage whatsoever, and from all costs, counsel
- ; fees, and liabilities incurred in or about any such claim, action, or
- ; proceeding brought thereon.
- ; 5. All materials and reports developed as a consequence of the use of
- ; this software shall duly acknowledge such use, in accordance with
- ; the usual standards of acknowledging credit in academic research.
- ;
- ; Status: A prototype version, but working fine.
- ;
- ; Tested: Macintosh II Coral/Allegro 16-Apr-89 15:49:59 Dan Suthers
- ;
- ; Changes:
- ; 24-Jun-90 DS Temporary hack around table search problem; see @ BUG.
- ;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; DOCUMENTATION FOR EXTERNAL USERS
- ;
- ; The User's View:
- ;
- ; The user sees something looking like a Fred window with text displayed
- ; in it. The user may move and resize the window, use the scroll bars,
- ; and select regions with the mouse. The contents of the window cannot
- ; be altered: a beep and a pop-up message window responds to attempts to
- ; do so (or to undefined keystrokes). Only a small number of keys, in
- ; particular including the Help key, may be meaningfully used.
- ;
- ; A Hyper-Display window may have zero or more commands associated with it
- ; for operating on selected regions. (It is up to the application whether
- ; these commands expand the region into a new hypertext structure to be
- ; displayed, or performs some other operation.) The user can always access
- ; any of these commands by using the Help key. If there is no selected
- ; region, this key pops up a general description of how to use HyperDisplay.
- ; If there is a selection, Help puts up a menu of defined commands. Some
- ; commands may have other keystrokes associated with them, for direct access
- ; without using the menu. The activated command is applied to the smallest
- ; region enclosing the currently selected text, or to the next region if
- ; there is no selection.
- ;
- ; The Client Program's View:
- ;
- ; To create a Hyper-Display, the application must give a hyper-structure and
- ; a set of commands to the function CREATE-HYPER-DISPLAY.
- ;
- ; A Hyper-structure is a Common Lisp structure. (The program interface is
- ; designed so that client programs may be written in pure Common Lisp,
- ; not being forced to deal with the object language used to implement this.)
- ; It has three client-accessible fields:
- ; Text-Specs: a list of strings and/or recursive hyper-structures.
- ; The text string to be displayed in the window is constructed by
- ; traversing the top level text-specs, concatenating strings and
- ; recursively converting embedded hyper-structures into text.
- ; Parent: backpointer to the hyper-structure whose text-specs this
- ; structure is embedded in. Note: the present code makes no use
- ; of this slot, and does not attempt to guarantee that its contents
- ; are correct. Its maintenance is up to the application.
- ; Object: a pointer to the application-specific data structure the text
- ; corresponds to. This is presumably used by the command functions to
- ; respond to queries, e.g. if the object is a node in a text plan,
- ; then a "define" query may be responded to by displaying text
- ; defining the associated concept.
- ;
- ; The application specifies the commands as a list of tuples:
- ; ( (<key> <description> <method>) *)
- ; where:
- ; <key> is a character specifying the command key which invokes the
- ; command (#\^E for Help if the command is only available on the menu)
- ; <description> is a short string describing the command (appropriate
- ; for menu display)
- ; <method> is a lambda form or function of two arguments which performs
- ; the desired operation. The arguments are the most specific hyper-
- ; structure which includes the text the user selected, and the window
- ; object containing the text.
- ;
- ; External Functions:
- ;
- ; CREATE-HYPER-DISPLAY is given the hyper-structure and command list, and
- ; creates and returns a window object, which should be retained for
- ; other functions operating on the window.
- ;
- ; DISPLAY-HYPER-STRUCTURE sets a window's hyper-structure to one specified,
- ; and sets the window's buffer to the resulting text, so that is will be
- ; displayed if and when the window is visible.
- ;
- ; MAKE-HYPER-STRUCTURE, HYPER-STRUCTURE-TEXT-SPECS, HYPER-STRUCTURE-PARENT,
- ; HYPER-STRUCTURE-OBJECT, COPY-HYPER-STRUCTURE, and HYPER-STRUCTURE-P:
- ; Defined by defstruct.
- ;
- ; HYPER-STRUCTURE-TEXT: returns a string constructed from recursive
- ; traversal of the hyper-structure-specs. Useful for debugging.
- ;
- ; The application is responsible for other features such as maintaining
- ; context stacks of hyper-structures for push/pop facilities, etc.
- ;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; Pending Improvements:
- ;
- ; Fix mouse click bug so window-mouse-up-event-handler called inside text
- ; region (not just mini-buffer). (However, there may be an advantage to
- ; leaving it this way: commands that want to know *exactly* what is
- ; selected can operate.)
- ;
- ; Rewrite to not construct a string of the entire buffer's text. Instead,
- ; write the text into the buffer on the fly. Then I don't need to risk
- ; modifying strings with trim-right-margin, and it is faster.
- ;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (in-package :HYPER-DISPLAY)
-
- (use-package :CCL)
-
- (export '(
- *Hyper-Display-window*
-
- create-hyper-display
- display-hyper-structure
-
- hyper-structure
- copy-hyper-structure
- hyper-structure-object
- hyper-structure-p
- hyper-structure-parent
- hyper-structure-text
- hyper-structure-text-specs
- make-hyper-structure
-
- ))
-
- (require :DIALOGUE)
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; PARAMETERS
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (eval-when (compile eval)
-
- (defconstant *DEFAULT-RIGHT-MARGIN* 75)
-
- (defconstant *DEFAULT-MINI-BUFFER-UNSELECTED-MESSAGE*
- "Use Help key for instructions.")
-
- (defconstant *DEFAULT-MINI-BUFFER-SELECTED-MESSAGE*
- "Use Help key for menu of commands.")
-
- (defconstant *DEFAULT-MENU-MESSAGE*
- "Choose an action to apply to your selection:")
-
- (defconstant *READ-ONLY-MESSAGE*
- "Hyper-Display windows are Read-Only (the text can't
- be modified). Push the Help key for instructions.")
-
- (defconstant *DEFAULT-GENERAL-INSTRUCTIONS*
- "You are using a HyperDisplay window, which allows you to perform
- certain actions on the displayed text. To use the display, first
- use the mouse to select the text you have a question about or want
- to perform an action on. (Do this the usual way: holding down the
- mouse and moving it across the text. You don't have to select all
- of the region exactly, you only have to select most of it and the
- HyperDisplay will use the smallest region enclosing what you have
- selected.) Then press the Help key. You will be shown a menu of
- actions you can take: select the one desired. Some actions can be
- invoked directly by keystrokes: there is an option on the Help menu
- to find out what these keystrokes are. The contents of this window
- cannot be modified, except by the defined actions.")
-
- ) ; eval-when
-
- (defconstant *HYPER-DISPLAY-PACKAGE* (find-package :hyper-display))
-
- (defvar *WINDOW-NAME-COUNTER* 0)
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; DATA TYPES
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Hyper-Structures
-
- (defstruct HYPER-STRUCTURE
- (TEXT-SPECS nil :type list)
- (PARENT nil :type hyper-structure)
- (OBJECT nil)
- (SELECTION-START 0 :type fixnum) ; Boundaries of buffer substring within
- (SELECTION-END 0 :type fixnum)) ; which the structure is displayed.
-
- ;;; This makes the structure appear to have another slot: the expansion of
- ;;; the text-specs. Not used by the present code, but very useful.
-
- (defun HYPER-STRUCTURE-TEXT (hs)
- "hyper-structure-text <hyper-structure>
- Returns the string which would be displayed for the given structure."
- (declare (type hyper-structure hs)
- (optimize (safety 1) (space 2) (speed 3)))
- ;; Reduce list of strings to one string.
- (reduce #'(lambda (s1 s2)
- (declare (string s1 s2))
- (concatenate 'string s1 s2))
- ;; Get a list of strings (all recursive specs expanded).
- (mapcar #'(lambda (spec)
- (if (stringp spec)
- spec
- (hyper-structure-text spec)))
- (hyper-structure-text-specs hs))
- :initial-value ""))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Region-Tables
- ;;;
- ;;; Region-tables are nested lists which enable a fast mapping from the
- ;;; start and end of a selected region of text in the buffer to the smallest
- ;;; underlying hyper-structure which includes the selected region. Padding
- ;;; of one character is allowed on each side of the region's actual text to
- ;;; allow for sloppy mouse manipulation. This should not produce ambiguity
- ;;; as long as regions are delimited by spaces, etc. An example of a region
- ;;; list for the string "This is a test." is given below -- assume that the
- ;;; symbols are really hyper-structures:
- ;;; ((9 . ((14 . test))) ; region "test" occurs in columns 10-13
- ;;; (7 . ((9 . a) (14 . a-test))) ; region "a" in 8-8; "a test" in 8-13
- ;;; (4 . ((7 . is))) ; region "is" in 5-6
- ;;; (-1 . ((4 . This)))) ; region "This" in 0-3
- ;;; Note that this includes nested regions for illustrative purposes.
- ;;; The structure of the table is as follows. The outer list indexes in
- ;;; decreasing order the start columns of the regions. The appropriate
- ;;; sub-list is identified by finding the largest sublist index which is
- ;;; smaller than the start of the range the user selected. Then, since
- ;;; several regions may start at the same column, a similar search is
- ;;; conducted in the sublist. These entries are in increasing order of
- ;;; the end column of the region, and the smallest index which is larger
- ;;; than the end column of the selected region is chosen. The CDR of this
- ;;; entry is the hyper-structure to be used.
-
- ;;; These are needed to build the lists.
- (eval-when (compile eval)
-
- (defmacro INSERT-CONS-INCREASING (the-cons the-list)
- `(setf ,the-list (merge 'list (list ,the-cons) ,the-list
- #'(lambda (cons1 cons2)
- (declare (cons cons1 cons2))
- (< (car cons1) (car cons2))))))
-
- (defmacro INSERT-CONS-DECREASING (the-cons the-list)
- `(setf ,the-list (merge 'list (list ,the-cons) ,the-list
- #'(lambda (cons1 cons2)
- (declare (cons cons1 cons2))
- (> (car cons1) (car cons2))))))
- ) ; eval-when
-
- ;;; The fundamental construction operation. It is assumed that the start
- ;;; and end values are already adjusted for the padding of 1 column.
-
- (defun ADD-REGION (region-start region-end region-structure region-list)
- (declare (fixnum region-start region-end) (list region-list)
- (type hyper-structure region-structure)
- (optimize (safety 1) (space 2) (speed 3)))
- (let ((existing-sublist (assoc region-start region-list))
- (region-entry (cons region-end region-structure)))
- (declare (list existing-sublist) (cons region-entry))
- (if existing-sublist
- (insert-cons-increasing region-entry (cdr existing-sublist))
- (insert-cons-decreasing (cons region-start (list region-entry))
- region-list))
- region-list))
-
- ;;; Search function for finding the appropriate region given start and end.
- ;;; @ BUG: If the start is in one sub-structure and the end in another sub
- ;;; structure, this returns nil even though there may be an enclosing super
- ;;; structure. Reason: it goes into the subentry indexed by the first sub's
- ;;; start position, but there is then no entry >= end in that subtable
- ;;; because the end is beyond its range. On this sort of failure, need to
- ;;; back down one entry in the table and try again (repeatedly). I "fixed"
- ;;; this with the hack of decrementing range-start and calling the search
- ;;; function recursively, repeating until range-start reaches 0 or the call
- ;;; returns a non-null region. THE REAL SOLUTION is to have each list in
- ;;; the table continued by the previous list -- that is, to hack the list
- ;;; structure at creation time so the search-region-table algorithm WITHOUT
- ;;; the recursive call hack works correctly by following a pointer to the
- ;;; previous list without knowing it.
-
- (defun SEARCH-REGION-TABLE (range-start range-end region-table)
- (declare (fixnum range-start range-end) (list region-table)
- (optimize (safety 1) (space 2) (speed 3)))
- (let ((ptr region-table))
- (declare (list ptr))
- ;; Search for appropriate subtable (list): the largest entry <= start.
- (loop
- (cond ((null ptr) (return))
- ((<= (car (first ptr)) range-start)
- (setq ptr (cdr (first ptr)))
- (return))
- (T (setq ptr (cdr ptr)))))
- ;; Search for appropriate entry in subtable: smallest entry >= end.
- (loop
- (cond ((null ptr) (return))
- ((>= (car (first ptr)) range-end)
- (setq ptr (cdr (first ptr)))
- (return))
- (T (setq ptr (cdr ptr)))))
- (or ptr
- (if (> range-start 0) ; @ temporary fix, see comment above
- (search-region-table (1- range-start) range-end region-table)))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Hyper-Display Windows
- ;;;
- ;;; These are Fred windows with the following changes:
- ;;; * Additional user-definable object-variables:
- ;;; - HYPER-STRUCTURE: the top level structure displayed in the window.
- ;;; - RIGHT-MARGIN: used to format text generated from hyper-structure.
- ;;; - MINI-BUFFER-MESSAGE: displayed in the mini-buffer to tell the
- ;;; user how to proceed.
- ;;; - MENU-MESSAGE: title for menu of available commands.
- ;;; * Additional internal object-variables:
- ;;; - COMMAND-TABLE: hash table constructed from the given command-list.
- ;;; - REGION-TABLE: for identifying selected region; see above.
- ;;; * All operations which modify the contents of the buffer are disabled.
- ;;; * Specified command keys invoke commands on the selected region.
-
- (defobject *HYPER-DISPLAY-WINDOW* *fred-window*)
-
- (defobfun (EXIST *Hyper-Display-window*) (init-list)
- (declare (object-variable command-table
- hyper-structure
- mini-buffer-selected-message
- mini-buffer-unselected-message
- general-instructions
- region-table))
- (let ((hyper-structure
- (getf init-list :hyper-structure (make-hyper-structure)))
- (command-list (getf init-list :command-list nil))
- (right-margin
- (getf init-list :right-margin *default-right-margin*))
- (menu-message
- (getf init-list :menu-message *default-menu-message*))
- (mini-buffer-selected-message
- (getf init-list :mini-buffer-selected-message
- *default-mini-buffer-selected-message*))
- (mini-buffer-unselected-message
- (getf init-list :mini-buffer-unselected-message
- *default-mini-buffer-unselected-message*))
- (general-instructions
- (getf init-list :general-instructions
- *default-general-instructions*)))
- (check-type right-margin fixnum)
- (check-type menu-message string)
- (check-type mini-buffer-selected-message string)
- (check-type mini-buffer-unselected-message string)
- (check-type general-instructions string)
- ;; These must be done first in case :window-show is specified T by user.
- ;; That causes display to occur early: the methods expect these to be "had".
- (have 'hyper-structure hyper-structure)
- (have 'region-table nil) ; temporary (until text displayed)
- (have 'command-table (make-hash-table)) ; keystrokes to (<string> <function>)
- (have 'right-margin right-margin)
- (have 'menu-message menu-message)
- (have 'mini-buffer-selected-message mini-buffer-selected-message)
- (have 'mini-buffer-unselected-message mini-buffer-unselected-message)
- (have 'general-instructions general-instructions)
- ;; Now it is safe to make the thing exist as a Fred Window.
- (usual-exist
- (init-list-default
- init-list
- :window-title (format nil "Hyper Display ~A"
- (incf *window-name-counter*))
- :package *hyper-display-package*
- ;; Position and size inherited from Fred.
- :window-show nil
- :window-font '("monaco" 12)
- :window-type :document-with-zoom
- :close-box t))
- ;; Set up the command tables; compute text (put in buffer) and region table.
- (set-command-table command-list)
- (layout-hyper-display)
- (set-mini-buffer mini-buffer-unselected-message)
- ;; Now show it, unless user specified not to.
- (if (getf init-list :window-show t) (window-show))
- ;; Return object created.
- (self)))
-
- ;;;------------------------------------------------------------------------
- ;;; These are needed to keep the message in the mini-buffer. They call the
- ;;; corresponding usual method (which writes size or position change message
- ;;; into the buffer), then rewrite the desired message into the buffer.
-
- (defobfun (SET-WINDOW-SIZE *Hyper-Display-window*) (h &optional (v nil))
- (declare (object-variable mini-buffer-selected-message
- mini-buffer-unselected-message))
- (funcall (ask *fred-window* (symbol-function 'set-window-size)) h v)
- (multiple-value-bind (start end) (selection-range)
- (declare (fixnum start end))
- (if (= start end)
- (set-mini-buffer mini-buffer-unselected-message)
- (set-mini-buffer mini-buffer-selected-message))))
-
- (defobfun (SET-WINDOW-POSITION *Hyper-Display-window*) (h &optional (v nil))
- (declare (object-variable mini-buffer-selected-message
- mini-buffer-unselected-message))
- (funcall (ask *fred-window* (symbol-function 'set-window-position)) h v)
- (multiple-value-bind (start end) (selection-range)
- (declare (fixnum start end))
- (if (= start end)
- (set-mini-buffer mini-buffer-unselected-message)
- (set-mini-buffer mini-buffer-selected-message))))
-
- ;;;------------------------------------------------------------------------
- ;;; Some standard methods are redefined to prevent modification of the text.
-
- (eval-when (compile eval) ; this is used by window-key-event-handler also
- (defmacro INFORM-READ-ONLY ()
- ;; Used to warn the user in response to undefined keystrokes or attempts
- ;; to modify the buffer.
- `(progn
- (ccl:ed-beep)
- (wind:message-dialogue ,*read-only-message*)))
- )
-
- (defobfun (CUT *Hyper-Display-window*) () (inform-read-only))
- (defobfun (PASTE *Hyper-Display-window*) () (inform-read-only))
-
- ;;;------------------------------------------------------------------------
- ;;; Methods for building and modifying Hyper-Display data structures.
-
- (defobfun (SET-HYPER-STRUCTURE *Hyper-Display-window*) (structure)
- (declare (object-variable hyper-structure region-table))
- (buffer-delete (window-buffer)
- :start 0
- :length (buffer-size (window-buffer)))
- (setq hyper-structure structure)
- (layout-hyper-display)
- structure)
-
- (defobfun (SET-COMMAND-TABLE *Hyper-Display-window*) (command-list)
- ;; Command list of form ((<char> <string> <function>)*).
- ;; Hash table associates <char> to (<string> <function>), except
- ;; that <char> of #\^E (Help key) has a list of all known
- ;; (<string> <function>) tuples, regardless of whether recorded
- ;; under a key.
- (declare (object-variable command-table) (list command-list)
- (optimize (safety 1) (space 2) (speed 3)))
- (clrhash command-table)
- ;; Record all commands in order given, for menu specification,
- ;; followed by menu-only command which lists keystrokes for commands.
- (setf (gethash #\^E command-table)
- (nconc (mapcar #'rest command-list)
- (list `("Show Keystrokes for Commands"
- (lambda (hs hd) (declare (ignore hs hd))
- (wind:message-dialogue
- ,(format nil "Keystrokes and Commands:~:{~%~A ~A~}"
- (remove #\^E command-list :key #'first))))))))
- ;; Record those having a key other than the help key under their keys.
- (dolist (command-spec command-list)
- (declare (list command-spec))
- (unless (char= (first command-spec) #\^E)
- (setf (gethash (first command-spec) command-table)
- (rest command-spec)))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; MAJOR INTERNAL FUNCTIONALITY
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defobfun (LAYOUT-HYPER-DISPLAY *Hyper-Display-Window*) ()
- (declare (object-variable hyper-structure region-table right-margin)
- (list region-table) (fixnum right-margin))
- (multiple-value-bind
- (region-list buffer-string fill-column)
- (compute-regions hyper-structure '() "" 0)
- (declare (list region-list) (string buffer-string)
- (fixnum fill-column)
- (optimize (safety 1) (space 2) (speed 3)))
- (setf region-table region-list)
- ;; Record start and end of top level structure's selection region.
- (setf (hyper-structure-selection-start hyper-structure) 0)
- (setf (hyper-structure-selection-end hyper-structure) (1- fill-column))
- ;; Insert the string. Note this assumes an empty buffer.
- (buffer-insert (window-buffer)
- (wind:trim-right-margin buffer-string right-margin)
- 0)))
-
- ;;; Function for constructing the region-tables.
- ;;; Returns three values: a constructed region-table (called region-list to
- ;;; avoid confusion with an object variable) for the structure, a string
- ;;; containing the text of the structure's region, and the fill-column at
- ;;; which concatenation to that string would continue (i.e. the length of
- ;;; the string). Padding around regions is handled here.
-
- (defun COMPUTE-REGIONS (structure region-list buffer-string initial-fill-column)
- (declare (type hyper-structure structure) (list region-list)
- (string buffer-string) (fixnum initial-fill-column)
- (optimize (safety 1) (space 2) (speed 3)))
- ;; Iterate down text-specs to construct the tree & text at this level.
- (do ((spec-ptr (hyper-structure-text-specs structure) (cdr spec-ptr))
- (fill-column initial-fill-column))
- ((null spec-ptr) (values region-list buffer-string fill-column))
- (declare (list spec-ptr) (fixumn fill-column))
- (cond
- ;; Strings are just appended to the buffer, with no region construction.
- ((stringp (first spec-ptr))
- (setq buffer-string (concatenate 'string buffer-string (first spec-ptr)))
- (incf fill-column (length (first spec-ptr))))
- (t
- ;; Subregions handled by building text into buffer recursively, then
- ;; recording the region in the table after we know where the region ends.
- (let ((subregion-start (max (1- fill-column) 0))) ; save padded beginning
- (declare (fixnum subregion-start))
- (multiple-value-setq
- (region-list buffer-string fill-column)
- (compute-regions (first spec-ptr) region-list buffer-string fill-column))
- ;; Record the range in the region structure itself, and in the table.
- (setf (hyper-structure-selection-start (first spec-ptr)) subregion-start)
- (setf (hyper-structure-selection-end (first spec-ptr)) fill-column)
- (setq region-list
- (add-region subregion-start fill-column ; already 1+ the region end
- (first spec-ptr) region-list)))))))
-
- ;;; Redefine low-level keystroke handling to bypass Fred's comtabs and invoke
- ;;; our own legal command keys, or notify user if undefined key used.
-
- (defobfun (WINDOW-KEY-EVENT-HANDLER *Hyper-Display-window*) (c)
- (declare (object-variable command-table general-instructions menu-message
- mini-buffer-unselected-message (self))
- (optimize (safety 1) (space 2) (speed 3)))
- (cond
-
- ((char= c #\^E) ; Help key.
- ;; If there is no selection, put up general instructions. Otherwise,
- ;; Put up a menu of actions (documentation strings are in the first
- ;; position) and call the selected function (second position) on the
- ;; selected sub-structure. (All commands were stored under #\^E.)
- (multiple-value-bind (start end) (selection-range)
- (declare (fixnum start end))
- (if (= start end)
- (wind:message-dialogue general-instructions)
- (funcall
- (second (assoc (wind:menu-dialogue
- (mapcar #'first (gethash #\^E command-table))
- menu-message)
- (gethash #\^E command-table)))
- (selected-hyper-structure)
- (self)))))
-
- ((member c '(#\ #\ #\ #\ #\ #\))
- ;; Let left, right, up, down arrows, page-up, and page-down be
- ;; interpreted by Fred normally.
- (funcall (ask *fred-window* (symbol-function 'window-key-event-handler))
- c (self))
- (set-mini-buffer mini-buffer-unselected-message))
-
- (T
- ;; Other keys only legal if found in command table.
- (let ((command-entry (gethash c command-table)))
- (declare (list command-entry))
- (if command-entry
- (funcall (second command-entry) (selected-hyper-structure) (self))
- (inform-read-only))))))
-
- ;;; Identifying the smallest hyper-structure which encloses selected region
- ;;; or follows the cursor. If none found, it is the whole text.
-
- (defobfun (SELECTED-HYPER-STRUCTURE *Hyper-Display-Window*) ()
- (declare (object-variable region-table hyper-structure)
- (optimize (safety 1) (space 2) (speed 3)))
- (multiple-value-bind
- (start end) (selection-range)
- (declare (fixnum start end))
- (decf end) ; it should point to last used, not the one after.
- (or (search-region-table start end region-table) hyper-structure)))
-
- ;;; When the user releases the mouse, the selection is expanded to show
- ;;; the user what the selected region is. BUG in CCL: only activated
- ;;; when mouse in the mini-buffer! Maybe this is OK?
-
- (defobfun (WINDOW-MOUSE-UP-EVENT-HANDLER *Hyper-Display-Window*) ()
- (declare (object-variable mini-buffer-selected-message
- mini-buffer-unselected-message))
- (let ((hyper-structure (selected-hyper-structure)))
- (set-mark (window-cursor-mark)
- (hyper-structure-selection-start hyper-structure))
- (set-selection-range
- ;; Special case guards against selecting off end of buffer.
- (if (= (buffer-size (window-buffer))
- (hyper-structure-selection-end hyper-structure))
- (hyper-structure-selection-end hyper-structure)
- (1+ (hyper-structure-selection-end hyper-structure))))
- (set-mini-buffer mini-buffer-selected-message)))
-
- ;;; Define this to highlight contained region when double clicked.
-
- (defobfun (WINDOW-CLICK-EVENT-HANDLER *Hyper-Display-Window*) (where)
- (declare (fixnum where)
- (object-variable mini-buffer-selected-message
- mini-buffer-unselected-message))
- (if (double-click-p)
- (let ((hyper-structure (selected-hyper-structure)))
- (set-mark (window-cursor-mark)
- (hyper-structure-selection-start hyper-structure))
- (set-selection-range
- ;; Special case guards against selecting off end of buffer.
- (if (= (buffer-size (window-buffer))
- (hyper-structure-selection-end hyper-structure))
- (hyper-structure-selection-end hyper-structure)
- (1+ (hyper-structure-selection-end hyper-structure))))
- (set-mini-buffer mini-buffer-selected-message))
- (progn
- (set-mini-buffer mini-buffer-unselected-message)
- (funcall (ask *fred-window* (symbol-function 'window-click-event-handler))
- where))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; EXPORTED FUNCTIONS
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun CREATE-HYPER-DISPLAY (text-structure command-list &rest init-list)
- "create-hyper-display <hyper-structure> <command-list>
- &rest <keyword-args>
- [*Hyper-Display-Window* Function]
- Creates a Hyper-Display window with text and commands as specified.
- The <keyword-args> can be any keyword/value pairs defined for Fred
- windows (eg. :window-size, :window-position, :window-title ...),
- plus the following additions:
- :right-margin - fixnum indicating the maximum width of each line of
- text generated from the <hyper-structure>.
- :menu-message - string labeling the pop-up menu of commands.
- :mini-buffer-selected-message
- :mini-buffer-unselected-message - strings displayed in the mini-buffer
- when there is and isn't a selection, respectively. These should
- remind the user how the display is used.
- :general-instructions - string displayed when user asks for help
- when there is no selection.
- The created window object is returned."
- (check-type text-structure hyper-structure)
- (check-type command-list list)
- (assert (evenp (length init-list)) (init-list)
- "Bad keyword/argument list (odd number of elements):~%~S" init-list)
- (do ((init-ptr init-list (cddr init-ptr)))
- ((null init-ptr))
- (assert (typep (first init-ptr) 'keyword) (init-list)
- "Non-keyword where keyword expected:~%~S" (first init-ptr)))
- (apply #'oneof *hyper-display-window*
- :hyper-structure text-structure
- :command-list command-list
- init-list))
-
- (defun DISPLAY-HYPER-STRUCTURE (text-structure hyper-display-window)
- "display-hyper-structure <hyper-structure> <hyper-display-window>
- [*Hyper-Display-Window* Function]
- Changes the hyper-structure of the window to the indicated structure,
- and displays the corresponding text."
- (check-type text-structure hyper-structure)
- (assert (typep hyper-display-window *hyper-display-window*)
- (hyper-display-window) "~S is not a *hyper-display-window*")
- (ask hyper-display-window
- (set-hyper-structure text-structure)
- (window-update)))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (provide :Hyper-Display)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; The End.